/*
 * Copyright 2024 Hangzhou Yingyi Technology Co., Ltd
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0

 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include <raspi/gpio.h>
#include <uk/assert.h>
#include <uk/print.h>
#include <uk/ofw/fdt.h>
#include <libfdt.h>

/* Macros to access gpio Registers with base address 'gpio_mem_addr'*/
#define GPIO_REG(r)		((uint32_t *)(gpio_mem_addr + (r)))
#define GPIO_REG_READ(r)	ioreg_read32(GPIO_REG(r))
#define GPIO_REG_WRITE(r, v)	ioreg_write32(GPIO_REG(r), v)

/*store base address of gpio*/
static uint64_t gpio_mem_addr;
static uint8_t gpio_initialized;

static void wait_cycles(unsigned int n)
{
	if (n) {
		while (n--)
			asm volatile("nop");
	}
}

void _libraspiplat_init_gpio(void *fdtp)
{
	int gpio_offset, rc;
	uint64_t addr, size;

	gpio_offset = fdt_node_offset_by_compatible(fdtp, -1,
							"brcm,bcm2835-gpio");

	if (gpio_offset < 0)
		UK_CRASH("No gpio device found!\n");

	rc = fdt_get_address(fdtp, gpio_offset, 0, &addr, &size);
	if (rc < 0)
		UK_CRASH("Could not find gpio address!\n");

	gpio_mem_addr = addr;
	gpio_initialized = 1;
}

int map_uart_to_gpio(void)
{
	if (!gpio_initialized) {
		UK_CRASH("GPIO not initialized!\n");
		return -1;
	}
	register uint32_t r;
	/* map UART0 to GPIO pins */
	r = GPIO_REG_READ(GPFSEL1);
	r &= ~((7 << 12) | (7 << 15)); // gpio14, gpio15
	r |= (4 << 12) | (4 << 15);    // alt0
	GPIO_REG_WRITE(GPFSEL1, r);
	GPIO_REG_WRITE(GPPUD, 0);
	wait_cycles(150);
	GPIO_REG_WRITE(GPPUDCLK0, (1 << 14) | (1 << 15));
	wait_cycles(150);
	GPIO_REG_WRITE(GPPUDCLK0, 0);
	return 0;
}

